﻿/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is BarocStudio.
*
* The Initial Developer of the Original Code is Kevin Ortman.
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */

using System;
using System.Collections.Generic;
using System.Threading;
using Irony.Parsing;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;

namespace BarocLanguage.Tagger
{
    internal sealed class BarocTokenTagger : ITagger<BarocTokenTag>
    {
        private readonly IDictionary<TokenColor, BarocTokenTypes> _BarocTypes;
        private readonly Parser _parser;

        internal BarocTokenTagger(ITextBuffer buffer)
        {
            _BarocTypes = new Dictionary<TokenColor, BarocTokenTypes>();
            _BarocTypes[TokenColor.Text] = BarocTokenTypes.Text;
            _BarocTypes[TokenColor.Keyword] = BarocTokenTypes.Keyword;
            _BarocTypes[TokenColor.Comment] = BarocTokenTypes.Comment;
            _BarocTypes[TokenColor.Identifier] = BarocTokenTypes.Identifier;
            _BarocTypes[TokenColor.String] = BarocTokenTypes.String;
            _BarocTypes[TokenColor.Number] = BarocTokenTypes.Number;


            Grammar grammar = new BarocGrammar();
            var language = new LanguageData(grammar);
            _parser = new Parser(language);
            Parse(buffer.CurrentSnapshot);
        }

        public event EventHandler<SnapshotSpanEventArgs> TagsChanged
        {
            add { }
            remove { }
        }

        public IEnumerable<ITagSpan<BarocTokenTag>> GetTags(NormalizedSnapshotSpanCollection spans)
        {

            foreach (SnapshotSpan curSpan in spans)
            {
                int startLoc = curSpan.Start.Position;
                int endLoc = curSpan.End.Position;
                Parse(curSpan.Snapshot);

                foreach (var token in _parseTree.Tokens)
                {
                    if ((token.Location.Position < startLoc) || (token.Location.Position > endLoc))
                        continue; 
                    
                    if (token.Terminal.Name.Equals("EOF"))
                        break;

                    if (token.Terminal.Name.Equals("SYNTAX_ERROR"))
                        continue;

                    if (_BarocTypes.ContainsKey(token.Terminal.EditorInfo.Color))
                    {
                        var tokenSpan = new SnapshotSpan(curSpan.Snapshot, new Span(token.Location.Position, token.Length));
                        if (tokenSpan.IntersectsWith(curSpan))
                            yield return new TagSpan<BarocTokenTag>(tokenSpan,
                                                                  new BarocTokenTag(_BarocTypes[token.Terminal.EditorInfo.Color]));
                    }
                }
            }

        }

        private ParseTree _parseTree = null;
        private Timer _parseTimer = null;

        private void Parse(ITextSnapshot snapshot)
        {
            if (_parseTree == null)
            {
                DoParse(snapshot);
                return;
            }

            if(_parseTimer != null)
                _parseTimer.Dispose();

            _parseTimer = new Timer(DoParse, snapshot, 500, Timeout.Infinite);
        }

        private void DoParse(object snapshot)
        {
            ITextSnapshot textSnapshot = snapshot as ITextSnapshot;
            if (textSnapshot == null) 
                return;

            _parseTree = _parser.Parse(textSnapshot.GetText());
        }
    }

}
